package com.ruben;/**
 * @ClassName: StreamDemo
 * @Date: 2020/11/21 0021 01:27
 * @Description:
 */

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruben.dao.MpUserMapper;
import com.ruben.pojo.po.UserPO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName: StreamDemo
 * @Description: 我还没有写描述
 * @Date: 2020/11/21 0021 1:27
 * *
 * @author: <achao1441470436@gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
public class StreamDemo {

    private static MpUserMapper mpUserMapper;

    /**
     * 判断是否被关注
     *
     * @return
     */
    static Boolean focusStatus(User user) {
        // Optional 这个类可以解决经常遇到的NPE(NullPointerException)
        // 如果传入的user为空
        user = Optional.ofNullable(user)
                // 我们就用建造者模式再建一个新的返回，建造者模式使用lombok的话就非常容易，不过要注意带上无参、全参构造
                .orElse(User.builder().name("阿超").age(19).build());
        System.out.println("我关注了" + user.getName());
        return true;
    }

    public static void main(String[] args) {
        // 生成1-100
        List<Integer> list = Stream.iterate(1, i -> ++i).limit(200).collect(Collectors.toList());
        System.out.println(list);
        // 使用collect函数进行转换为List<Map<Integer,Integer>>，包含值和线程id
        List<Map<String, Object>> result = list.stream().parallel().collect(() -> {
            System.out.println("第一个参数：Supplier，我们返回一个带初始值的List，放进去三个负数");
            Map<String, Object> map1 = new HashMap<>();
            // 获取线程id
            long threadId = Thread.currentThread().getId();
            // 放入一个值为负数，只有一个元素的map
            map1.put("value", -1);
            map1.put("threadId", threadId);
            List<Map<String, Object>> currentList = new ArrayList<>();
            // 将map添加进去
            currentList.add(map1);
            return currentList;
        }, (lastList, value) -> {
            // 具体的聚合操作，将value封装为map，加上线程id，放入list
            HashMap<String, Object> map = new HashMap<>();
            map.put("value", value);
            map.put("threadId", Thread.currentThread().getId());
            lastList.add(map);
        }, (lastList, currentList) -> {
            // 并行流下合计上面多个结果
            System.out.println("lastList：" + lastList);
            lastList.addAll(currentList);
            System.out.println("currentList：" + currentList);
        });
        System.out.println("最终结果：" + result);
        System.out.println("最终结果个数：" + result.size());
    }

    private static void sum() {
        List<Integer> list = Stream.generate(() -> 1).limit(100).collect(Collectors.toList());
        System.out.println(list);
        int sum = list.stream().reduce(1, Integer::sum, (a, b) -> {
            System.out.println("stream执行sum时上次结果：[" + a + "]本次值：[" + b + "]");
            return Integer.sum(a, b);
        });
        System.out.println(sum);
    }

    private static void parallelSum() {
        List<Integer> list = Stream.generate(() -> 1).limit(100).collect(Collectors.toList());
        System.out.println(list);
        AtomicInteger count = new AtomicInteger(0);
        int sum = list.parallelStream().reduce(1, Integer::sum, (a, b) -> {
            System.out.println("parallelStream执行sum时上次结果：[" + a + "]本次值：[" + b + "]");
            count.incrementAndGet();
            return Integer.sum(a, b);
        });
        System.out.println(sum);
        System.out.println(count.get());
    }

    private static void timeCost() {
        List<Integer> list = new SecureRandom().ints().limit(10).boxed().collect(Collectors.toList());
        AtomicInteger lastValue = new AtomicInteger();
        long startTime = System.nanoTime();
        // 求和操作
        int sum = list.stream().mapToInt(integer -> {
            lastValue.set(integer);
            System.out.println("普通stream最新结果" + lastValue.get());
            return integer.intValue();
        }).reduce(0, Integer::sum);
        System.out.println("普通stream求和结果：" + sum);
        long normalStreamEndTime = System.nanoTime();

        System.out.println("普通stream耗时：" + ((normalStreamEndTime - startTime) / (1000.0 * 1000.0)) + " ms");
        // 求和操作
        int parallelSum = list.parallelStream().mapToInt(integer -> {
            lastValue.set(integer);
            System.out.println("parallelStream最新结果" + lastValue.get());
            return integer.intValue();
        }).reduce(0, Integer::sum);
        System.out.println("parallelStream求和结果：" + parallelSum);
        long parallelStreamEndTime = System.nanoTime();
        System.out.println("parallelStream耗时：" + ((parallelStreamEndTime - normalStreamEndTime) / (1000.0 * 1000.0)) + " ms");
    }

    private static void flatMapDemo() {
        List<String> nineNine = Stream.iterate(1, i -> ++i)
                .flatMap(i -> Stream.iterate(i, in -> ++in).map(in -> i + "*" + in + "=" + i * in).limit(10 - i))
                .limit(45)
                .collect(Collectors.toList());
        nineNine.forEach(System.out::println);
        List<String> abc = Stream.iterate('a', i -> ++i)
                .map(String::valueOf)
                .limit(26)
                .flatMap(i -> Stream.concat(Stream.of(i), Stream.of(i).map(String::toUpperCase)))
                .sorted()
                .collect(Collectors.toList());
        System.out.println(abc);
        List<Character> ab = Stream.iterate('A', i -> ++i)
                .limit(58)
                .filter(i -> i < 91 || i > 96)
                .collect(Collectors.toList());
        System.out.println(ab);
        List<User> userList = new ArrayList<>();
        userList.add(User.builder().id("用户id").parentId("父id").build());
        List<String> userIds = userList.parallelStream()
                .flatMap(u -> Stream.of(u.getId(), u.getParentId()))
                .collect(Collectors.toList());
        System.out.println(userIds);
        List<String> a1 = Stream.iterate('A', a -> a++).limit(26).map(String::valueOf).collect(Collectors.toList());
        List<String> strings = a1.stream().peek(a -> a = a.toLowerCase(Locale.ROOT)).collect(Collectors.toList());
        System.out.println(strings);
        String str = "你去";
        System.out.println(str);
    }

    private static void addAllDemo() {
        AtomicInteger i = new AtomicInteger(2);
        AtomicReference<BigDecimal> j = new AtomicReference<>(BigDecimal.ZERO);
        j.set(BigDecimal.TEN);
        List<String> stringList = new ArrayList<>(10);
        if (i.get() > 3) {
            stringList.addAll(mpUserMapper.selectList(Wrappers.lambdaQuery())
                    .stream()
                    .map(UserPO::getUsername)
                    .distinct()
                    .collect(Collectors.toList()));
        }
        List<String> strings = new ArrayList<>(10);
        strings.stream().peek(s -> {
            stringList.add(s);
            i.set(3);
            j.get().add(BigDecimal.ONE);
        }).collect(Collectors.toList());
    }

    private static void streamDemo() {
        /*-------------------------------------------------常用---------------------------------------------------*/
        // 首先是forEach()
        StringList list = new StringList();
        list.put("")
                .put("张三")
                .put("李四")
                .put("王五")
                .put("赵六")
                .put("陈七")
                .put("秦八")
                .put("ruben")
                .put("ruben")
                .put("007")
                .put("");
        // 以前遍历我们的forEach是这样的
        for (String item : list) {
            System.out.println(item);
            /**(打印结果)
             * 张三
             * 李四
             * 王五
             * 赵六
             * 陈七
             * 秦八
             * ruben
             * ruben
             * 007
             */
        }
        // 现在用新版forEach() 打印结果同上
        list.forEach(System.out::println);
        // 然后是map() 以及collect()
        // 我们要更改list里元素的类型，比如原来的List<User>
        List<User> userList = new ArrayList<>();
        User user1 = new User();
        user1.setName("狂神");
        user1.setAge(23);
        userList.add(user1);
        // 现在需要增加返回一个参数，判断这个人有没有被我关注
        List<Map<String, Object>> userMapList = userList.stream().map(
                //这里的user就相当于userList里每一个user
                user -> {
                    //new一个临时变量
                    Map<String, Object> tempMap = new HashMap<>(16);
                    tempMap.put("name", user.getName());
                    tempMap.put("age", user.getAge());
                    //调用方法判断是否关注这个人
                    tempMap.put("isFocus", focusStatus(user));
                    //返回
                    return tempMap;
                }).collect(Collectors.toList());
        // 输出结果 -> {isFocus=true, name=狂神, age=23}
        userMapList.forEach(System.out::println);
        // 把所有年龄替换为19
        List<User> users = userList.stream().peek(user -> user.setAge(19)).collect(Collectors.toList());

        // 筛选出为空的字符串，获取个数
        System.out.println(list.stream().filter(""::equals).count());
        // 并行流，Stream流中的一种，异步实现
        System.out.println(list.parallelStream().filter(String::isEmpty).count());
        // filter和removeIf差不多，不过removeIf会影响源数据
//        list.removeIf(data -> !data.isEmpty());

        // skip limit 和sql里的limit相似，可以截取数据数量
        // 取出下标从3开始往后6条数据，并且筛选不为空的元素
        list.stream().skip(3).limit(6).filter(string -> !"".equals(string)).forEach(System.out::println);

        //distinct()去除重复元素 joining() 在之间添加元素
        System.out.println(list.stream().distinct().collect(Collectors.joining(" 和 ")));

        // 拿到所有年龄，转换为int[]数组
        int[] ages = users.stream().mapToInt(User::getAge).toArray();
        for (int age : ages) {
            System.out.print(age + " ");
        }

        // 快速取出26个大写英文字母
        List<String> A = Stream.iterate("A", e -> String.valueOf((char) (e.charAt(0) + 1)))
                .limit(26)
                .collect(Collectors.toList());
        System.out.println(A);
        // 快速取出26个小写英文字母，拼接成字符串，使用逗号隔开
        String a = Stream.iterate("a", e -> String.valueOf((char) (e.charAt(0) + 1)))
                .limit(26)
                .collect(Collectors.joining(","));
        System.out.println(a);
        // 对取出来的小写英文字母使用逗号分隔
        List<String> strings = Pattern.compile(",").splitAsStream(a).collect(Collectors.toList());
        strings.forEach(System.out::print);
        System.out.println();
        // 打印10个“啊”
        String str = Stream.generate(() -> "啊").limit(10).collect(Collectors.joining());
        System.out.println(str);

        // 使用建造者模式构建一个Stream并打印出每个元素
        Stream.builder().add("ruben").add("achao").build().forEach(System.out::println);

        /*-------------------------------------------------toMap---------------------------------------------------*/

        // 转换成Map<用户名,用户对象>
        Map<String, User> userMap = userList.stream().collect(Collectors.toMap(User::getName, Function.identity()));
        userMap.forEach((username, user) -> System.out.println(username + " " + user.getAge()));
        // 转换成Map<用户名,年龄>
        Map<String, Integer> stringIntegerMap = userList.stream()
                .collect(Collectors.toMap(User::getName, User::getAge));
        stringIntegerMap.forEach((username, age) -> System.out.println(username + " " + age));
        // 转换成Map<用户名,年龄>
        stringIntegerMap = userList.stream()
                .collect(HashMap::new, (map, user) -> map.put(user.getName(), user.getAge()), HashMap::putAll);
        stringIntegerMap.forEach((username, age) -> System.out.println(username + " " + age));



        /*-------------------------------------------------排序---------------------------------------------------*/

        //sorted排序
        List<Integer> integerList = new ArrayList<>();
        integerList.add(2);
        integerList.add(1);
        // 自然数正序
        integerList.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
        // 倒序
        integerList.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
        // 自定义排序
        integerList.stream().sorted((o1, o2) -> {
            return o1.compareTo(o2);
        }).forEach(System.out::println);
        // 乱序，和Collections.shuffle差不多，不过shuffle会影响源数据
        integerList.stream().sorted((o1, o2) -> new SecureRandom().nextInt(2) - 1).collect(Collectors.toList());
        //        Collections.shuffle(integerList);

        /*-------------------------------------------------运算---------------------------------------------------*/

        // 此类的实例用于生成伪随机数流
        SecureRandom random = new SecureRandom();
        // 用于收集统计信息（例如计数，最小值，最大值，总和和平均值）的状态对象
        IntSummaryStatistics intSummaryStatistics = random.ints().limit(10).summaryStatistics();
        System.out.println("最小:" + intSummaryStatistics.getMin());
        System.out.println("最小，没找到则抛异常:" + random.ints()
                .limit(20)
                .min()
                .orElseThrow(() -> new RuntimeException("没找到最小值")));
        System.out.println("平均:" + intSummaryStatistics.getAverage());
        System.out.println("平均，没找到则为0:" + random.ints().limit(20).average().orElse(0));
        System.out.println("最大:" + intSummaryStatistics.getMax());
        System.out.println("最大，没找到则为0:" + random.ints().limit(20).max().orElse(0));
        System.out.println("总数量:" + intSummaryStatistics.getCount());
        System.out.println("总数量:" + random.ints().limit(20).count());
        System.out.println("求和:" + intSummaryStatistics.getSum());
        System.out.println("求和:" + random.ints().limit(20).sum());
        // 对象运算 boxed() 为快速装箱
        // 求和 reduce返回的Optional
        System.out.println("求和:" + random.ints()
                .limit(10)
                .boxed()
                .reduce(Integer::sum)
                .orElseThrow(() -> new RuntimeException("求和失败")));
        System.out.println("求和2:" + random.ints().limit(10).boxed().reduce(0, Integer::sum));
        System.out.println("求和3:" + random.ints().limit(10).boxed().reduce(0, Integer::sum, Integer::sum));

        /*-------------------------------------------------判断/取某个值---------------------------------------------------*/

        // 转换为list
        List<Integer> someNumber = random.ints().limit(20).boxed().collect(Collectors.toList());
        // 取出第一条，没取到则为0,开发中经常使用，避免了数组越界或者NPE(空指针)等
        System.out.println(someNumber.stream().findFirst().orElse(0));
        // 随机取一个，没取到则为0
        System.out.println(someNumber.stream().findAny().orElse(0));
        // 直接取和，没取到则为0
        System.out.println(someNumber.stream().reduce(Integer::sum).orElse(0));
        // 不包含20为true
        System.out.println(someNumber.stream().noneMatch(data -> data == 20));
        // 全部大于0为true
        System.out.println(someNumber.stream().allMatch(data -> data > 0));
        // 任何一个元素等于0为true
        System.out.println(someNumber.stream().anyMatch(data -> data.equals(0)));

        /*-------------------------------------------------分组---------------------------------------------------*/

        // 根据奇数偶数分组
        Map<Boolean, List<Integer>> map = integerList.stream().collect(Collectors.groupingBy(data -> data % 2 == 0));
        List<Integer> evenNumbers = map.get(true);
        List<Integer> oddNumber = map.get(false);
        System.out.println("偶数-----------");
        evenNumbers.forEach(System.out::println);
        System.out.println("奇数-----------");
        oddNumber.forEach(System.out::println);

        /*-------------------------------------------------二维数组List互转---------------------------------------------------*/
        // 二维数组int[][]转List<List<Integer>>
        int[][] array = new int[][]{{1, 3}, {2, 4}, {5, 6}};
        List<List<Integer>> collect = Arrays.stream(array)
                .map(a1 -> Arrays.stream(a1).boxed().collect(Collectors.toList()))
                .collect(Collectors.toList());
        // 输出结果
        collect.forEach(System.out::print);
        // List<List<Integer>>转int[][]
        array = collect.stream()
                .map(integers -> integers.stream().mapToInt(value -> value).toArray())
                .toArray(int[][]::new);
        // 输出结果
        for (int[] ints : array) {
            for (int anInt : ints) {
                System.out.println(anInt);
            }
        }
        List<Integer> integers = Arrays.stream(new int[]{1, 2, 3}).boxed().collect(Collectors.toList());
        // 连接两个List
        List<Integer> integers1 = Stream.concat(integers.stream(), integers.stream()).collect(Collectors.toList());
        // 打印
        integers1.forEach(System.out::print);

        // 第二种方式连接
        integers1 = Stream.of(integers, integers).flatMap(List::stream).collect(Collectors.toList());
        // 打印
        integers1.forEach(System.out::print);

        /*-------------------------------------------------链表和list互转---------------------------------------------------*/
        // 定义链表
        class ListNode {
            final int val;
            ListNode next;

            public ListNode(int x) {
                val = x;
            }
        }

        // 创建链表
        ListNode zero = new ListNode(0);
        ListNode one = new ListNode(1);
        ListNode two = new ListNode(2);
        ListNode three = new ListNode(3);
        // 0->1->2->3
        two.next = three;
        one.next = two;
        zero.next = one;

        // 获取链表长度，为什么要获取链表长度呢？因为下面使用的无限流，在jdk8的时候是没法通过条件判断停止，所以这里使用limit去截取，jdk9的时候就可以不用下面的获取长度操作了
        ListNode tmp = zero;
        AtomicInteger length = new AtomicInteger();
        while (tmp != null) {
            length.getAndIncrement();
            tmp = tmp.next;
        }
        // 链表转换成List<Integer>
        List<Integer> integers2 = Stream.iterate(zero, l -> l.next)
                .limit(length.get())
                .mapToInt(l -> l.val)
                .boxed()
                .collect(Collectors.toList());
        // 链表转换成int[]
        int[] ints = Stream.iterate(zero, l -> l.next).limit(length.get()).mapToInt(l -> l.val).toArray();
        // List<Integer>转换成链表
        ListNode listNode = integers2.stream()
                // 可以在这里进行倒序排序，以证明我们确实转换成功
                .sorted(Comparator.reverseOrder())
                .collect(() -> new ListNode(0), (listNode1, integer) -> {
                    ListNode tmp1 = listNode1;
                    while (tmp1.next != null) {
                        tmp1 = tmp1.next;
                    }
                    tmp1.next = new ListNode(integer);
                }, (listNode12, listNode2) -> Function.identity());
        // 打印
        tmp = listNode;
        while (tmp != null) {
            System.out.println(tmp.val);
            tmp = tmp.next;
        }
    }

    /**
     * @ClassName: User
     * @Date: 2020/6/21 0021 21:16
     * @Description: 实体类
     * @Author: <achao1441470436@gmail.com>
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class User implements Serializable {
        private String name;
        private Integer age;
        private String id;
        private String parentId;
    }

    /**
     * @ClassName: StringList
     * @Date: 2020/6/21 0021 21:08
     * @Description: 一个继承了ArrayList<String>的类
     * @Author: <achao1441470436@gmail.com>
     */
    public static class StringList extends ArrayList<String> {

        public StringList put(String s) {
            super.add(s);
            // 用于链式编程
            return this;
        }
    }

}
